﻿//
// <author>David Nohejl</author>
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WPFTrees.Parser;
using System.Windows.Controls;
using System.Windows;
using WPFTrees.DataStructures;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Windows.Media;
using WPFTrees.Tools;

namespace WPFTrees.Layouts
{
    /// <summary>
    /// Tradicni layout "Cladogram".    
    /// </summary>
    public class CladogramLayout : LayoutBase, ISupportGroups
    {
        Point _root;
        
        double bottom;
        double alpha2;
        List<NTreeNode<NewickNode>> _leaves;

        /// <summary>
        /// Vytvori novou instanci Cladogram.
        /// </summary>        
        public CladogramLayout()
        {                        
            _isRooted = false;
            _branchDrawer = new StraightLineBranchDrawer(Brushes.Black);
        }

        double childReservedWidth; // sirka prostoru pro jeden list
        Canvas _canvas;

        /// <inheritdoc />
        /// <remarks>
        /// </remarks>
        public override void ExecuteLayout(Canvas canvas, NTree<NewickNode> tree)
        {
            _leaves = tree.Info.Leaves;
            double width = canvas.RenderSize.Width;
            double height = canvas.RenderSize.Height;
            bool zeroSize = (width * height == 0);
            if (zeroSize)
                return;
            double halfWidth = canvas.RenderSize.Width / 2.0;
            Point rootPos = new Point(halfWidth, 0);
            alpha2 = Math.Atan2(halfWidth, canvas.RenderSize.Height);
            Size size = canvas.RenderSize;
            _canvas = canvas;
            _root = rootPos;
            Tree = tree;
            bottom = size.Height;
            childReservedWidth = size.Width / (_leaves.Count-1);

            ExecuteLayoutRecursive(tree.GetRoot(), rootPos, 0, size.Width);
        }

        /// <inheritdoc />        
        public Shape CreateGroupShape(NewickNode node)
        {
            Polygon groupPoly = new Polygon();            
            groupPoly.Points = new PointCollection();
            groupPoly.Fill = new SolidColorBrush(node.GroupInfo.Color);
            groupPoly.Opacity = 0.5;
            groupPoly.Name = node.Label;
            return groupPoly;
        }

        /// <summary>
        /// Aktualizuje vizualni prvek znazornujici skupinu.
        /// </summary>
        /// <param name="node">Group node.</param>
        /// <param name="nodePos">Position of group node.</param>
        /// <param name="leftPt">Left corner coordinates of group.</param>
        /// <param name="rightPt">Right corner coordinates of group.</param>
        public void UpdateGroupShape(NewickNode node, Point nodePos, 
            Point leftPt, Point rightPt)
        {
            Polygon groupPoly = node.GroupInfo.Shape as Polygon;
            groupPoly.Points = new PointCollection(
                new Point[] { nodePos, leftPt, rightPt });
        }

        /// <summary>
        /// Traverse tree and recompute canvas coordinates.
        /// </summary>
        /// <param name="root">Root node</param>
        /// <param name="rootPos">position of root node</param>
        /// <param name="left">x-coordinate of reserved space</param>
        /// <param name="right">y=coordinate of reserved space</param>
        void ExecuteLayoutRecursive(NTreeNode<NewickNode> root, Point rootPos,
            double left, double right)
        {
            NewickNode node = root.Value();
            FrameworkElement element = node.VisualElement;          
            
            // position root
            Canvas.SetLeft(element, rootPos.X);
            Canvas.SetTop(element, rootPos.Y);

            Tools.TreeTools.DetermineLabelVisibility(node);

            if (node.IsCollapsed)
            {
                // collapsed root
                return;
            }

            double x = left;
            int count = root.CountImmediateChildren;

            for(int i =0; i<count;i++)
            {
                NTreeNode<NewickNode> item = root.Children[i];
                                        
                    if (item == Tree.SelectedNode)
                    {
                        Tools.TreeTools.SelectNode(item);
                    }

                    NewickNode childNode = item.Value();

                    if (item.CountImmediateChildren == 0)
                    {                       
                        #region Leaf

                        // schovani/zobrazeni labelu                            
                        Tools.TreeTools.DetermineLabelVisibility(childNode);

                        Point nodePos = new Point(x,bottom);
                        // nastavi souradnice listu
                        //childNode.VisualElement.UpdateLayout();
                        Canvas.SetTop(childNode.VisualElement, nodePos.Y);
                        Canvas.SetLeft(childNode.VisualElement, nodePos.X);                        
                       
                        _branchDrawer.PositionBranch(
                            childNode.BranchToParentElement, node, childNode,
                            rootPos, nodePos);
                        
                        // move left by one slot
                        x += childReservedWidth;
                        #endregion

                    }
                    else
                    {
                        #region subtree
                        // compute subtree width
                        double newLeft = x;
                        double newRight = x + (item.Leaves-1)*childReservedWidth;
                        // posune left o sirku podstromu + jeden slot
                        
                        double newRootX = newLeft+ (newRight - newLeft) / 2;
                        //Vector v0 = new Vector(x - _root.X, _root.Y + bottom);
                        x = newRight + childReservedWidth;
                        double newWidth = (newRight - newLeft);
                        double newRootY = bottom - (newWidth / 2) / Math.Tan(alpha2);

                        Debug.Assert(newRootY >= 0, "y>0");

                        #region group
                        if (childNode.GroupInfo != null)
                        {
                            UpdateGroupShape(childNode,
                                new Point(newRootX, newRootY),
                                new Point(newLeft, bottom),
                                new Point(newRight, bottom));
                        }                            
                            
                        #endregion

                        
                        Line line = childNode.BranchToParentElement as Line;

                        Point nodePos = new Point(newRootX, newRootY);

                        _branchDrawer.PositionBranch(line, node, childNode,
                            rootPos, nodePos);

                        ExecuteLayoutRecursive(item, nodePos, newLeft, newRight);

                        #endregion
                    }                
            }          
        }       
    }
}
